package gateway.server.engine;


import gateway.api.engine.Engine;
import gateway.config.Config;
import gateway.internal.api.message.DefaultContext;
import gateway.internal.api.message.DefaultMessage;
import gateway.server.lifecycle.LiftCycleFactory;
import gateway.server.route.Route;
import gateway.server.route.RouteInitializer;
import gateway.server.route.RouteManagement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.SortedSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Jin Zheng
 * @since 1.0 2020-09-10
 */
public class RouteEngine implements Engine
{
	private static Logger logger = LoggerFactory.getLogger(RouteEngine.class);

	private final Config config;
	private final ExecutorService executor;
	private final RouteManagement routeManagement = new RouteManagement();
	private final LiftCycleFactory liftCycleFactory;

	public RouteEngine(Config config)
	{
		this.config = config;
		this.executor = Executors.newFixedThreadPool(100);
		this.liftCycleFactory = new LiftCycleFactory(this);
	}

	@Override
	public void dispatch(String connectorId, DefaultMessage message)
	{
		SortedSet<Route> routes = routeManagement.getRoutes(connectorId);
		if (routes == null || routes.isEmpty())
		{

			return;
		}

		executor.submit(() -> this.send(routes, message));

	}

	private void send(SortedSet<Route> routes, DefaultMessage message)
	{
		for (Route route : routes)
		{
			if (!route.match(message))
				continue;

			DefaultContext context = new DefaultContext(executor, route.getArgs());
			message.setContext(context);
			DefaultChain chain = new DefaultChain(executor, route, message);
			chain.doNext();
		}
	}

	@Override
	public void initialize() throws Exception
	{
		RouteInitializer initializer = new RouteInitializer(routeManagement);
		initializer.setup(config);
		for (Object object : routeManagement.objects())
		{
			liftCycleFactory.setAware(object);
			liftCycleFactory.initialize(object);
		}
	}


	@Override
	public void destroy() throws Exception
	{
		logger.info("destroy");
		this.executor.shutdown();
		for (Object object : routeManagement.objects())
		{
			liftCycleFactory.destroy(object);
		}
	}

	@Override
	public ExecutorService getExecutorService()
	{
		return executor;
	}

	public Config getConfig()
	{
		return config;
	}

	public RouteManagement getRouteManagement()
	{
		return routeManagement;
	}
}
